﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Runtime.Serialization;

namespace TotalCrytography.Protector
{
    [Serializable()]
    public class Symmetric_Provider : ISerializable
    {
        private SymmetricAlgorithm m_Algorithm;

        public SymmetricAlgorithm Algorithm
        {
            get { return m_Algorithm; }
            set { m_Algorithm = value; }
        }
        private PaddingMode m_paddingMode;

        public PaddingMode PaddingMode
        {
            get { return m_paddingMode; }
            set { m_paddingMode = value; }
        }
        private CipherMode m_cipherMode;

        public CipherMode CipherMode
        {
            get { return m_cipherMode; }
            set { m_cipherMode = value; }
        }
        private byte[] m_key;

        public byte[] Key
        {
            get { return m_key; }
            set { m_key = value; }
        }

        private byte[] m_iv;

        public byte[] IV
        {
            get { return m_iv; }
            set { m_iv = value; }
        }

        public string GetIV()
        {
            return Encoding.Default.GetString(m_iv);
        }
        public string GetKey()
        {
            return Convert.ToBase64String(m_key);
        }

        public Symmetric_Provider()
        {
            m_Algorithm = new RijndaelManaged();
            m_cipherMode = CipherMode.CBC;
            m_paddingMode = PaddingMode.PKCS7;
            GenerateKeyAndIV();
        }

        public Symmetric_Provider(string strKey)
        {
            m_Algorithm = new RijndaelManaged();
            m_cipherMode = CipherMode.CBC;
            m_paddingMode = PaddingMode.PKCS7;
            m_key = GetLegalKey(strKey);
            m_Algorithm.GenerateIV();
            m_iv = m_Algorithm.IV;
        }

        public Symmetric_Provider(string strKey, string strIV)
        {
            m_Algorithm = new RijndaelManaged();
            m_cipherMode = CipherMode.CBC;
            m_paddingMode = PaddingMode.PKCS7;
            m_key = GetLegalKey(strKey);
            m_iv = Convert.FromBase64String(strIV);
        }

        public Symmetric_Provider(SymmetricAlgorithm algorithm, PaddingMode paddingmode, CipherMode ciphermode)
        {
            m_Algorithm = algorithm;
            m_cipherMode = ciphermode;
            m_paddingMode = paddingmode;
            GenerateKeyAndIV();
        }

        public Symmetric_Provider(string strKey, SymmetricAlgorithm algorithm, PaddingMode paddingmode, CipherMode ciphermode)
        {
            m_Algorithm = algorithm;
            m_cipherMode = ciphermode;
            m_paddingMode = paddingmode;
            m_key = GetLegalKey(strKey);
            m_Algorithm.GenerateIV();
            m_iv = m_Algorithm.IV;
        }

        public Symmetric_Provider(string strKey, string strIV,SymmetricAlgorithm algorithm, PaddingMode paddingmode, CipherMode ciphermode)
        {
            m_Algorithm = algorithm;
            m_cipherMode = ciphermode;
            m_paddingMode = paddingmode;
            m_key = GetLegalKey(strKey);
            m_iv = Encoding.Default.GetBytes(strIV);
        }

        public void GenerateKeyAndIV()
        {
            if (m_Algorithm != null)
            {
                m_Algorithm.GenerateKey();
                m_key = m_Algorithm.Key;
                m_Algorithm.GenerateIV();
                m_iv = m_Algorithm.IV;
            }
        }

        public byte[] GetLegalKey(string mKey)
        {
            // Adjust m_key if necessary, and return a valid m_key
            if (m_Algorithm.LegalKeySizes.Length > 0)
            {
                // Key sizes in bits
                int keySize = mKey.Length * 8;
                int minSize = m_Algorithm.LegalKeySizes[0].MinSize;
                int maxSize = m_Algorithm.LegalKeySizes[0].MaxSize;
                int skipSize = m_Algorithm.LegalKeySizes[0].SkipSize;

                if (keySize > maxSize)
                {
                    // Extract maximum size allowed
                    mKey = mKey.Substring(0, maxSize / 8);
                }
                else if (keySize < maxSize)
                {
                    int validSize = (keySize <= minSize) ? minSize :
                         (keySize - keySize % skipSize) + skipSize;
                    if (keySize < validSize)
                    {
                        // Pad the m_key with asterisk to make up the size
                        mKey = mKey.PadRight(validSize / 8, '*');
                    }
                }
            }
            return ASCIIEncoding.Default.GetBytes(mKey);
        }

        public string EncryptString(string input)
        {
            m_Algorithm.Key = m_key;
            byte[] inputByte = ASCIIEncoding.Default.GetBytes(input);
            ICryptoTransform encryptor = m_Algorithm.CreateEncryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream encryptorStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
            encryptorStream.Write(inputByte, 0, inputByte.Length);
            encryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            encryptorStream.Close();
            return GetIV() + (char)1 + (char)2 + (char)3 + Convert.ToBase64String(outputByte);
        }

        public string EncryptString(string input, string key)
        {
            m_Algorithm.Key = GetLegalKey(key);
            byte[] inputByte = ASCIIEncoding.Default.GetBytes(input);
            ICryptoTransform encryptor = m_Algorithm.CreateEncryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream encryptorStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
            encryptorStream.Write(inputByte, 0, inputByte.Length);
            encryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            encryptorStream.Close();
            return GetIV() + (char)1 + (char)2 + (char)3 + Convert.ToBase64String(outputByte);
        }

        public string DecryptString(string input)
        {
            m_Algorithm.Key = m_key;
            string f = "" +  (char)1 + (char)2 + (char)3;
            int p = input.IndexOf(f);
            string ivString = input.Substring(0, p);
            input = input.Substring(p + 3);
            byte[] iv = Encoding.Default.GetBytes(ivString);
            m_Algorithm.IV = iv;
            byte[] inputByte = Convert.FromBase64String(input);
            ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
            decryptorStream.Write(inputByte, 0, inputByte.Length);
            decryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            decryptorStream.Close();
            return ASCIIEncoding.Default.GetString(outputByte);
        }

        public string DecryptString(string input, string key)
        {
            m_Algorithm.Key = GetLegalKey(key);
            string f = "" + (char)1 + (char)2 + (char)3;
            int p = input.IndexOf(f);
            string ivString = input.Substring(0, p);
            input = input.Substring(p + 3);
            byte[] iv = Convert.FromBase64String(ivString);
            m_Algorithm.IV = iv;
            byte[] inputByte = Convert.FromBase64String(input);
            ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
            decryptorStream.Write(inputByte, 0, inputByte.Length);
            decryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            decryptorStream.Close();
            return ASCIIEncoding.Default.GetString(outputByte);
        }

        // MSND
        public bool EncryptFile(string infile, string outfile)
        {
            FileStream inputStream = new FileStream(infile, FileMode.Open, FileAccess.Read);
            FileStream outputStream = new FileStream(outfile, FileMode.Create, FileAccess.Write);

            CryptoStream encryptorStream = null;
            try
            {
                m_Algorithm.Padding = m_paddingMode;
                m_Algorithm.Mode = m_cipherMode;
                ICryptoTransform encryptor = m_Algorithm.CreateEncryptor();
                encryptorStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
                byte[] byteBuffer = new byte[256];
                long nTotalByteInput = inputStream.Length;
                long nTotalByteWritten = 0;
                int nCurReadLen = 0;
                while (nTotalByteWritten < nTotalByteInput)
                {
                    nCurReadLen = inputStream.Read(byteBuffer, 0, byteBuffer.Length);
                    encryptorStream.Write(byteBuffer, 0, nCurReadLen);
                    nTotalByteWritten += nCurReadLen;
                }
                encryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
            }
            catch (Exception e)
            {
                if (encryptorStream != null)
                    encryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
                throw new Exception(e.Message);
            }
            return true;
        }

        public bool DecryptFile(string infile, string outfile)
        {
            FileStream inputStream = new FileStream(infile, FileMode.Open, FileAccess.Read);
            FileStream outputStream = new FileStream(outfile, FileMode.Create, FileAccess.Write);

            CryptoStream decryptorStream = null;
            try
            {
                m_Algorithm.IV = m_iv;
                m_Algorithm.Key = m_key;
                m_Algorithm.Padding = m_paddingMode;
                m_Algorithm.Mode = m_cipherMode;
                ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
                decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
                byte[] byteBuffer = new byte[256];
                long nTotalByteInput = inputStream.Length;
                long nTotalByteWritten = 0;
                int nCurReadLen = 0;
                while (nTotalByteWritten < nTotalByteInput)
                {
                    nCurReadLen = inputStream.Read(byteBuffer, 0, byteBuffer.Length);
                    decryptorStream.Write(byteBuffer, 0, nCurReadLen);
                    nTotalByteWritten += nCurReadLen;
                }
                decryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
            }
            catch (Exception e)
            {
                if (decryptorStream != null)
                    decryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
                throw new Exception(e.Message);
            }
            return true;
        }

        public bool DecryptFile(string infile, string outfile, string strIV)
        {
            FileStream inputStream = new FileStream(infile, FileMode.Open, FileAccess.Read);
            FileStream outputStream = new FileStream(outfile, FileMode.Create, FileAccess.Write);

            CryptoStream decryptorStream = null;
            try
            {
                m_Algorithm.IV = Convert.FromBase64String(strIV);
                m_Algorithm.Key = m_key;
                m_Algorithm.Padding = m_paddingMode;
                m_Algorithm.Mode = m_cipherMode;
                ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
                decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
                byte[] byteBuffer = new byte[256];
                long nTotalByteInput = inputStream.Length;
                long nTotalByteWritten = 0;
                int nCurReadLen = 0;
                while (nTotalByteWritten < nTotalByteInput)
                {
                    nCurReadLen = inputStream.Read(byteBuffer, 0, byteBuffer.Length);
                    decryptorStream.Write(byteBuffer, 0, nCurReadLen);
                    nTotalByteWritten += nCurReadLen;
                }
                decryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
            }
            catch (Exception e)
            {
                if (decryptorStream != null)
                    decryptorStream.Close();
                inputStream.Close();
                outputStream.Close();
                throw new Exception(e.Message);
            }
            return true;
        }

        public byte[] EncryptBytes(byte[] inputByte)
        {
            m_Algorithm.Padding = m_paddingMode;
            m_Algorithm.Mode = m_cipherMode;
            m_Algorithm.Key = m_key;
            m_Algorithm.IV = m_iv;
            ICryptoTransform encryptor = m_Algorithm.CreateEncryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream encryptorStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write);
            encryptorStream.Write(inputByte, 0, inputByte.Length);
            encryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            encryptorStream.Close();
            return outputByte;
        }

        public byte[] DecryptBytes(byte[] inputByte)
        {
            try
            {
                m_Algorithm.Padding = m_paddingMode;
                m_Algorithm.Mode = m_cipherMode;
                m_Algorithm.Key = m_key;
                m_Algorithm.IV = m_iv;
                ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
                MemoryStream outputStream = new MemoryStream();
                CryptoStream decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
                decryptorStream.Write(inputByte, 0, inputByte.Length);
                decryptorStream.FlushFinalBlock();
                byte[] outputByte = outputStream.ToArray();
                outputStream.Close();
                decryptorStream.Close();
                return outputByte;
            }
            catch (Exception e)
            {
                return null;
            }
        }

        public byte[] DecryptBytes(byte[] inputByte, string strIV)
        {
            m_Algorithm.Padding = m_paddingMode;
            m_Algorithm.Mode = m_cipherMode;
            m_Algorithm.Key = m_key;
            m_Algorithm.IV = Convert.FromBase64String(strIV);
            ICryptoTransform decryptor = m_Algorithm.CreateDecryptor();
            MemoryStream outputStream = new MemoryStream();
            CryptoStream decryptorStream = new CryptoStream(outputStream, decryptor, CryptoStreamMode.Write);
            decryptorStream.Write(inputByte, 0, inputByte.Length);
            decryptorStream.FlushFinalBlock();
            byte[] outputByte = outputStream.ToArray();
            outputStream.Close();
            decryptorStream.Close();
            return outputByte;
        }


        public Symmetric_Provider(SerializationInfo info, StreamingContext ctxt)
        {
            //Get the values from info and assign them to the appropriate properties

            //EmpId = (int)info.GetValue("EmployeeId", typeof(int));
            //EmpName = (String)info.GetValue("EmployeeName", typeof(string));
            Algorithm = (SymmetricAlgorithm)info.GetValue("Algorithm", typeof(SymmetricAlgorithm));
            PaddingMode = (PaddingMode)info.GetValue("PaddingMode", typeof(PaddingMode));
            CipherMode = (CipherMode)info.GetValue("CipherMode", typeof(CipherMode));
            Key = (byte[])info.GetValue("Key", typeof(byte[]));
            IV = (byte[])info.GetValue("IV", typeof(byte[]));
        }
        
        //Serialization function.

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            //You can use any custom name for your name-value pair. But make sure you

            // read the values with the same name. For ex:- If you write EmpId as "EmployeeId"

            // then you should read the same with "EmployeeId"

            //info.AddValue("EmployeeId", EmpId);
            //info.AddValue("EmployeeName", EmpName);
            info.AddValue("Algorithm", Algorithm);
            info.AddValue("PaddingMode", PaddingMode);
            info.AddValue("CipherMode", CipherMode);
            info.AddValue("Key", Key);
            info.AddValue("IV", IV);
        }


        //public bool WriteBinary(String fileName, BinaryReader bw)
        //{
        //    try
        //    {
        //        switch (bw.ReadInt32())
        //        {
        //            case 0:
        //                m_Algorithm = new DESCryptoServiceProvider();
        //                break;
        //            case 1:
        //                m_Algorithm = new TripleDESCryptoServiceProvider();
        //                break;
        //            case 2:
        //                m_Algorithm = new RC2CryptoServiceProvider();
        //                break;
        //            case 3:
        //                m_Algorithm = new RijndaelManaged();
        //                break;
        //            default:
        //                break;
        //        }
        //        switch (bw.ReadInt32())
        //        {
        //            case 0:
        //                m_paddingMode = PaddingMode.ANSIX923;
        //                break;
        //            case 1:
        //                m_paddingMode = PaddingMode.PKCS7;
        //                break;
        //            case 2:
        //                m_paddingMode = PaddingMode.ISO10126;
        //                break;
        //        }
        //        switch (bw.ReadInt32())
        //        {
        //            case 0:
        //                _sp.CipherMode = CipherMode.ECB;
        //                break;
        //            case 1:
        //                _sp.CipherMode = CipherMode.CBC;
        //                break;
        //            case 2:
        //                _sp.CipherMode = CipherMode.OFB;
        //                break;
        //            case 3:
        //                _sp.CipherMode = CipherMode.CFB;
        //                break;
        //            case 4:
        //                _sp.CipherMode = CipherMode.CTS;
        //                break;
        //        }
        //        return true;
        //    }
        //    catch(Exception e)
        //    {
        //        return false;
        //    }
        //}
    }
}
